/*
 *  src/process_control/producer_consumer.c
 *
 *
 *  Harry Wei <harryxiyou@gmail.com> (C) 2011
 */
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/sem.h>

#include "semun.h"
#include "pv.h"

#undef P
#undef V

/*
 * The following program is just for demonstrating 
 * producer-consumer model with pv-operation concurrently.
 * This has been included by my libxyos, you can use it
 * by linking this library and include the relevant header
 * file. These codes are all under the GPL v3 or later 
 * version so feel free to change it for yourself.
 */

#if 0
#ifdef _SEMUN_H
#define _SEMNU_H
union semnu {
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	struct seminfo *__buf;
};
#endif

/*
 * p: the p operation for applying resource.
 * @para semid: the semaphore's id.
 * @para index: the semaphore's index.
 * @return: if error happens return -1, return 0 is right.
 */
int p(int semid, int index)
{
#if 1
	struct sembuf buf = {
		.sem_num = 0,			/*semaphore number*/
		.sem_op = -1,			/*semaphore operation*/
		.sem_flg = SEM_UNDO		/*operation flags*/
	};
#endif
	if (0 > index) {
		g_message("%s -- index of the semaphore is invalid!\n", __func__);
		return -1;
	}
	buf.sem_num = index;
	if (-1 == semop(semid, &buf, 1)) {
		g_message("%s -- process %d p operation failed!\n", __func__, getpid());
		return -1;
	}
	return 0;
}

/*
 * v: the v operation for releasing resource.
 * @para semid: the semaphore's id.
 * @para index: the semaphore's index.
 * @return: if error happens return -1, return 0 is right.
 */
int v(int semid, int index)
{
#if 1
	struct sembuf buf = {
		.sem_num = 0,
		/* release resource, add the buf.sem_op to semval */
		.sem_op = 1,
		.sem_flg = SEM_UNDO
	};
#endif
	if (0 > index) {
		g_message("%s -- index of the semaphore is invalid!\n", __func__);
		return -1;
	}
	buf.sem_num = index;
	if (-1 == semop(semid, &buf, 1)) {
		g_message("%s -- process %d v operation failed!\n", __func__, getpid());
		return -1;
	}
	return 0;
}
#endif

/*
 * producer_consumer_pv: use the pv operations for access cs.
 * @para pid_num: the amount of accessing process.
 * @return: if error happens return -1, return 0 is right.
 */
int producer_consumer_pv(uint32_t pid_num)
{
	pid_t pid;
	key_t key;
	uint32_t i = 0;
	uint32_t j = 0;
	uint32_t proj_id = 2;
	uint32_t semid = 0;
	union semun arg;

//	memset(&arg, 0, sizeof(union semun));
	/* convert a pathname and a project identifier to a system v ipc key */
	if (-1 == (key = ftok(".", proj_id))) {
		g_message("%s -- generating IPC key failed!\n", __func__);
		return -1;
	}
	/* get a semaphore set identifier */
	if (-1 == (semid = semget(key, 1, IPC_CREAT | 0666))) {
		g_message("%s -- creating semaphore set failed!\n", __func__);
		return -1;
	}
	arg.val = 1;
	/* semaphore control operations */
	if (-1 == semctl(semid, 0, SETVAL, arg)) {
		g_message("%s -- set semval failed!\n", __func__);
		return -1;
	}
	for (i = 0; i < pid_num; i++) {
		pid = fork();
		if (0 > pid) {
			g_message("%s -- fork error!\n", __func__);
			return -1;
		} else if (0 == pid) {
			if (-1 == (semid = semget(key, 1, 0))) {
				exit(EXIT_FAILURE);
			}
#if defined(P)
			if (-1 == p(semid, 0)) {
				exit(EXIT_FAILURE);
			}
#endif
			g_print("%s -- process %d enter the critical section!!!\n", __func__, getpid());
			sleep(1);
			g_print("%s -- process %d access the critical section!!!\n", __func__, getpid());
			sleep(1);
			g_print("%s -- process %d leave the critical section!!!\n", __func__, getpid());
			sleep(1);
#if defined(V)
			if (-1 == v(semid, 0)) {
				exit(EXIT_FAILURE);
			}
#endif
			return 0;
		}
	}
	for (i = 0; i < pid_num; i++) {
		wait(NULL);
	}
	if (-1 == semctl(semid, 0, IPC_RMID, 0)) {
		g_message("%s -- destroy the sem set failed!\n", __func__);
		return -1;
	}
	return 0;
}

